using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using ProjectEuler.Core.Helpers;

namespace ProjectEuler.Core
{
    public class Problem46 : IProjectEulerProblem
    {
        protected long SmallestOddComposite()
        {
            long num = 9;
            while(CanBeWrittenAsSumOfPrimeAndTwiceASquare(num))
            {
                num++;
                while(MathHelper.IsEven(num) || MathHelper.IsPrime(num))
                {
                    num++;
                }
            }
            return num;
        }

        protected bool CanBeWrittenAsSumOfPrimeAndTwiceASquare(long num)
        {
            var allprimesLessThanNum = new List<long>();
            for (long i = num - 1; i >= 2;i-- )
            {
                if(MathHelper.IsPrime(i))
                {
                    allprimesLessThanNum.Add(i);
                }
            }

            return allprimesLessThanNum.Any(prime => CanBeWrittenAsTwiceASquare(num - prime));
        }

        protected bool CanBeWrittenAsTwiceASquare(long num)
        {
            long i = 1;
            while(true)
            {
                var square = (long) Math.Pow(i, 2);
                var twotimessquare = 2*square;
                if (num == twotimessquare)
                {
                    return true;
                }
                if(twotimessquare > num)
                {
                    return false;
                }
                i++;
            }
        }

        public int Number
        {
            get { return 46; }
        }

        public string Description
        {
            get { return "What is the smallest odd composite that cannot be written as the sum of a prime and twice a square?"; }
        }

        public string Answer
        {
            get { return SmallestOddComposite().ToString(); }
        }
    }

    [TestFixture]
    public class Problem46Tests : Problem46
    {
        [Test]
        public void Then_9_through_33_can_be_written_that_way()
        {
            Assert.IsTrue(CanBeWrittenAsSumOfPrimeAndTwiceASquare(9));
            Assert.IsTrue(CanBeWrittenAsSumOfPrimeAndTwiceASquare(15));
            Assert.IsTrue(CanBeWrittenAsSumOfPrimeAndTwiceASquare(21));
            Assert.IsTrue(CanBeWrittenAsSumOfPrimeAndTwiceASquare(25));
            Assert.IsTrue(CanBeWrittenAsSumOfPrimeAndTwiceASquare(27));
            Assert.IsTrue(CanBeWrittenAsSumOfPrimeAndTwiceASquare(33));
        }

        [Test]
        public void Canbewrittenastwiceasquare_works()
        {
            Assert.IsTrue(CanBeWrittenAsTwiceASquare(2));
            Assert.IsTrue(CanBeWrittenAsTwiceASquare(8));
            Assert.IsTrue(CanBeWrittenAsTwiceASquare(18));

            Assert.IsFalse(CanBeWrittenAsTwiceASquare(17));
            Assert.IsFalse(CanBeWrittenAsTwiceASquare(7));
            Assert.IsFalse(CanBeWrittenAsTwiceASquare(5));
        }
    }
}